using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;

namespace Common.Unity.Extensions.PropertyMapper
{
	public static class ConversionHelper
	{
		public static object CheckForConversion( object source, PropertyInfo targetProperty, Type converterType )
		{
			return 
				Convert( 
					ResolvePropertyConverter( targetProperty ), 
					converterType, 
					source, 
					targetProperty.PropertyType );
		}

		class TypeConverterManager : IDisposable
        {
            private readonly Type targetType;
            private TypeDescriptionProvider descriptionProvider;

            public TypeConverterManager(Type targetType, Type converterType )
            {
                this.targetType = targetType;
                descriptionProvider = converterType != null ? TypeDescriptor.AddAttributes( targetType, new Attribute[] { new TypeConverterAttribute( converterType ) } ) : null;
            }

			void IDisposable.Dispose()
            {
                if (descriptionProvider != null)
                {
                    TypeDescriptor.RemoveProvider(descriptionProvider, targetType);
                    descriptionProvider = null;
                }
            }
        }


		/*
		public static object CheckForConversion( ITypeDescriptorContext typeContext, object source, Type targetType )
		{
			return Convert( typeContext, TypeDescriptor.GetConverter( targetType ), source != null ? TypeDescriptor.GetConverter( source.GetType() ) : null, source, targetType );
		}
*/
		public static bool IsConvertable( Type from, Type to )
		{
			TypeConverter converterFrom = TypeDescriptor.GetConverter( from ),
			              converterTo = TypeDescriptor.GetConverter( to );
			return from.IsAssignableFrom( to ) || converterFrom.CanConvertTo( to ) || converterTo.CanConvertFrom( from );
		}

		static object Convert( TypeConverter from, Type converterType, object source, Type targetType )
		{
			if ( source != null && from != null )
			{
				using ( new TypeConverterManager( targetType, converterType ) )
				{
					Type type = source.GetType();
					TypeConverter to = TypeDescriptor.GetConverter( type );
					if ( !targetType.IsAssignableFrom( type ) )
					{
						if ( from.CanConvertFrom( type ) )
						{
							object result = from.ConvertFrom( source );
							return result;
						}
						else if ( to.CanConvertTo( targetType ) )
						{
							object result = to.ConvertTo( source, targetType );
							return result;
						}
					}
				}
			}
			return source;
		}

		static TypeConverter ResolvePropertyConverter( PropertyInfo info )
		{
			PropertyDescriptor descriptor = TypeDescriptor.GetProperties( info.DeclaringType ).Find( info.Name, true );
			return descriptor != null ? descriptor.Converter : TypeDescriptor.GetConverter( info.PropertyType );
		}
	}
}